#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <types.h>
#include <assert.h>
#include <sys/vmm.h>
#include "gui_spirit.h"
#include "gui_signal.h"
#include "gui_surface.h"
#include "gui_window.h"
#include "gui_mouse.h"
#include "gui_timer.h"
#include "gui_button.h"
#include "gui_form_style.h"
#include "gui_keyboard.h"
#include "gui_color.h"
#include "gui_event.h"
#include "gui_label.h"
#include <uview.h>
#include <sys/ipc.h>

extern int __gui_has_window_close;

static gui_timer_t *gui_window_find_timer(gui_window_t *window, uint32_t timer_id);
static int gui_window_destroy_all_timer(gui_window_t *window);

static gui_window_style_t __gui_window_style_default = {
    1,
    24,
    GUI_RGB(245, 245, 245), // background
    GUI_RGB(225, 225, 225),
    GUI_RGB(230, 230, 230), // front
    GUI_RGB(200, 200, 200),
    GUI_RGB(200, 200, 200), // border
    GUI_RGB(180, 180, 180),
    GUI_RGB(58, 58, 58), // text
    GUI_RGB(118, 118, 118),
};

void gui_window_quit(gui_spirit_t *spirit)
{
    return gui_spirit_destroy_all(spirit);
}

void gui_window_calc_content_size(gui_window_t *window, int vwidth, int vheight, int *cwidth, int *cheight)
{
    int content_width;
    int content_height;

    if (window->type == GUI_WINDOW_TOPLEVEL)
    {
        content_width = vwidth - window->style->border_thick * 2;
        content_height = vheight - window->style->border_thick * 2 - window->style->navigation_height;
    }
    else
    {
        content_width = vwidth;
        content_height = vheight;
    }

    *cwidth = content_width;
    *cheight = content_height;
}

void gui_window_calc_view_size(gui_window_t *window, int cwidth, int cheight, int *vwidth, int *vheight)
{
    int view_width;
    int view_height;

    if (window->type == GUI_WINDOW_TOPLEVEL)
    {
        view_width = cwidth + window->style->border_thick * 2;
        view_height = cheight + window->style->border_thick * 2 + window->style->navigation_height;
    }
    else
    {
        view_height = cheight;
        view_width = cwidth;
    }

    *vwidth = view_width;
    *vheight = view_height;
}

static int gui_window_change_size(gui_window_t *window, int width, int height)
{
    if (!window)
        return -1;

    gui_spirit_t *spirit = &window->spirit;

    // calc content size
    int content_width, content_height;
    gui_window_calc_content_size(window, width, height, &content_width, &content_height);

    // no change
    if (window->content_width == content_width && window->content_height == height)
    {
        return 0;
    }

    window->content_height = content_height;
    window->content_width = content_width;

    if (gui_window_mmap_resize(window, width, height) < 0)
        return -1;

    gui_spirit_reset_size(spirit, content_width, content_height);
    if (window->type == GUI_WINDOW_TOPLEVEL)
    {
        gui_spirit_reset_size(&window->window_spirit, width, height);
        gui_window_draw_border(window, 1, 1);
    }
    else
    {
        if (window->type == GUI_WINDOW_POPUP)
        {
            gui_window_draw_no_border(window);
        }
    }

    // set mobile area size
    gui_window_reset_mobile_area(window);

    gui_spirit_show_children(spirit);

    // trigger window change signal
    gui_event_t event;
    event.type = GUI_WINRESIZE_EVENT;
    event.winresize.w = content_width;
    event.winresize.h = content_height;
    gui_signal_emit_arg(spirit, "resize_event", &event);

    return 0;
}

static int gui_window_resize(gui_window_t *window, uint32_t width, uint32_t height)
{
    uint32_t win_width, win_height;
    //dprintf("window resize size %d %d\n",width,height);
    gui_window_calc_view_size(window, width, height, &win_width, &win_height);
    uview_repair_size(&win_width, &win_height);
    return uview_resize(window->spirit.view, win_width, win_height);
}

int gui_mouse_filter_msg(gui_spirit_t *spirit, uview_msg_t *msg)
{
    uview_msg_get_mouse_x(msg) = uview_msg_get_mouse_x(msg) - spirit->x;
    uview_msg_get_mouse_y(msg) = uview_msg_get_mouse_y(msg) - spirit->y;
    int x = uview_msg_get_mouse_x(msg);
    int y = uview_msg_get_mouse_y(msg);
    if (x < 0 || y < 0 || x >= spirit->width || y >= spirit->height)
        return -1;
    int msg_id = uview_msg_get_id(msg);
    gui_event_t event;
    int tmpval = -1;
    switch (msg_id)
    {
    case UVIEW_MSG_MOUSE_MOTION:
        event.type = GUI_MOUSE_MOTION;
        event.motion.x = x;
        event.motion.y = y;
        if (gui_signal_emit_arg(spirit, "mouse_notify", &event))
            return -1;
        break;
    case UVIEW_MSG_MOUSE_LBTN_DCLICK:
    case UVIEW_MSG_MOUSE_LBTN_DOWN:
    case UVIEW_MSG_MOUSE_MBTN_DCLICK:
    case UVIEW_MSG_MOUSE_MBTN_DOWN:
    case UVIEW_MSG_MOUSE_RBTN_DCLICK:
    case UVIEW_MSG_MOUSE_RBTN_DOWN:
        if (msg_id == UVIEW_MSG_MOUSE_LBTN_DOWN || msg_id == UVIEW_MSG_MOUSE_LBTN_DCLICK)
        {
            tmpval = UVIEW_BTN_LEFT;
        }
        else
        {
            if (msg_id == UVIEW_MSG_MOUSE_MBTN_DCLICK || msg_id == UVIEW_MSG_MOUSE_MBTN_DOWN)
            {
                tmpval = UVIEW_BTN_MIDDLE;
            }
            else
            {
                if (msg_id == UVIEW_MSG_MOUSE_RBTN_DCLICK || msg_id == UVIEW_MSG_MOUSE_RBTN_DOWN)
                {
                    tmpval = UVIEW_BTN_RIGHT;
                }
            }
        }
        event.type = GUI_MOUSE_BUTTON_DOWN;
        event.button.status = GUI_PRESSED;
        event.button.button = tmpval;
        event.button.x = x;
        event.button.y = y;
        if (gui_signal_emit_arg(spirit, "button_press", &event))
            return -1;
        break;
    case UVIEW_MSG_MOUSE_LBTN_UP:
    case UVIEW_MSG_MOUSE_MBTN_UP:
    case UVIEW_MSG_MOUSE_RBTN_UP:
        if (msg_id == UVIEW_MSG_MOUSE_LBTN_UP)
        {
            tmpval = UVIEW_BTN_LEFT;
        }
        else
        {
            if (msg_id == UVIEW_MSG_MOUSE_MBTN_UP)
            {
                tmpval = UVIEW_BTN_MIDDLE;
            }
            else
            {
                if (msg_id == UVIEW_MSG_MOUSE_RBTN_UP)
                {
                    tmpval = UVIEW_BTN_RIGHT;
                }
            }
        }
        event.type = GUI_MOUSE_BUTTON_UP;
        event.button.status = GUI_RELEASE;
        event.button.button = tmpval;
        event.button.x = x;
        event.button.y = y;
        if (gui_signal_emit_arg(spirit, "button_release", &event))
            return -1;
        break;
    case UVIEW_MSG_MOUSE_WHEEL_UP:
    case UVIWE_MSG_MOUSE_WHEEL_DOWN:
    case UVIEW_MSG_MOUSE_WHEEL_LEFT:
    case UVIEW_MSG_MOUSE_WHEEL_RIGHT:
        if (msg_id == UVIEW_MSG_MOUSE_WHEEL_UP)
        {
            tmpval = GUI_WHEEL_UP;
        }
        else
        {
            if (msg_id == UVIWE_MSG_MOUSE_WHEEL_DOWN)
            {
                tmpval = GUI_WHEEL_DOWN;
            }
            else
            {
                if (msg_id == UVIEW_MSG_MOUSE_WHEEL_RIGHT)
                {
                    tmpval = GUI_WHEEL_RIGHT;
                }
                else
                {
                    if (msg_id == UVIEW_MSG_MOUSE_WHEEL_LEFT)
                    {
                        tmpval = GUI_WHEEL_LEFT;
                    }
                }
            }
        }
        event.type = GUI_MOUSE_WHEEL;
        event.wheel.wheel = tmpval;
        event.wheel.x = x;
        event.wheel.y = y;
        if (gui_signal_emit_arg(spirit, "button_scroll", &event))
            return -1;
        break;
    default:
        break;
    }
    return 0;
}

int gui_keyboard_filter_msg(gui_spirit_t *spirit, uview_msg_t *msg)
{
    gui_event_t event;
    int msg_id = uview_msg_get_id(msg);
    if (msg_id == UVIEW_MSG_KEY_DOWN)
    {
        event.type = GUI_KEY_DOWN;
        event.key.status = GUI_PRESSED;
        event.key.keycode.code = uview_msg_get_key_code(msg);
        event.key.keycode.modify = uview_msg_get_key_modify(msg);
        if (gui_signal_emit_arg(spirit, "key_press", &event))
            return -1;
    }
    else
    {
        if (msg_id == UVIEW_MSG_KEY_UP)
        {
            event.type = GUI_KEY_UP;
            event.key.status = GUI_RELEASE;
            event.key.keycode.code = uview_msg_get_key_code(msg);
            event.key.keycode.modify = uview_msg_get_key_modify(msg);
            if (gui_signal_emit_arg(spirit, "key_release", &event))
                return -1;
        }
    }
    return 0;
}

int gui_window_filter_msg(gui_window_t *window, uview_msg_t *msg)
{
    gui_spirit_t *spirit = &window->spirit;
    int msg_id = uview_msg_get_id(msg);
    switch (msg_id)
    {
    case UVIEW_MSG_MOUSE_MOTION:
    case UVIEW_MSG_MOUSE_LBTN_DCLICK:
    case UVIEW_MSG_MOUSE_LBTN_DOWN:
    case UVIEW_MSG_MOUSE_LBTN_UP:
    case UVIEW_MSG_MOUSE_RBTN_DCLICK:
    case UVIEW_MSG_MOUSE_RBTN_DOWN:
    case UVIEW_MSG_MOUSE_RBTN_UP:
    case UVIEW_MSG_MOUSE_MBTN_DCLICK:
    case UVIEW_MSG_MOUSE_MBTN_DOWN:
    case UVIEW_MSG_MOUSE_MBTN_UP:
    case UVIEW_MSG_MOUSE_WHEEL_RIGHT:
    case UVIEW_MSG_MOUSE_WHEEL_LEFT:
    case UVIWE_MSG_MOUSE_WHEEL_DOWN:
    case UVIEW_MSG_MOUSE_WHEEL_UP:
        if (!gui_mouse_filter_msg(spirit, msg))
            return 0;
        break;
    case UVIEW_MSG_KEY_DOWN:
    case UVIEW_MSG_KEY_UP:
        if (!gui_keyboard_filter_msg(spirit, msg))
            return 0;
        break;
    case UVIEW_MSG_HIDE:
        gui_mouse_motion(spirit, -1, -1);
        gui_spirit_hide(spirit);
        return 0;
        break;
    case UVIEW_MSG_SHOW:
        gui_mouse_motion(spirit, -1, -1);
        gui_spirit_show(spirit);
        return 0;
        break;
    case UVIEW_MSG_ACTIVATE:
        if (window->type == GUI_WINDOW_TOPLEVEL)
        {
            gui_window_set_active(window, true);
            gui_signal_emit_by_name(spirit, "active");
            return 0;
        }
        break;
    case UVIEW_MSG_INACTIVATE:
        if (window->type == GUI_WINDOW_TOPLEVEL)
        {
            gui_window_set_active(window, false);
            gui_signal_emit_by_name(spirit, "inactive");
            return 0;
        }
        break;
    case UVIEW_MSG_LEAVE:
        gui_signal_emit_by_name(spirit, "leave_notify");
        return 0;
        break;
    case UVIEW_MSG_ENTER:
        gui_signal_emit_by_name(spirit, "enter_notify");
        return 0;
        break;
    case UVIEW_MSG_RESIZE:
        // resize response
        gui_window_change_size(window, uveiew_msg_get_resize_width(msg), uview_msg_get_resize_height(msg));
        // mouse position change
        gui_mouse_motion(spirit, -1, -1);
        return 0;
        break;
    case UVIEW_MSG_PAINT:
        if (window->paint_callback)
        {
            gui_rect_t rect;
            if (!gui_window_get_invalid(window, &rect))
            {
                if (gui_rect_valid(&rect))
                {
                    window->paint_callback(spirit, &rect);
                    return 0;
                }
            }
        }
        break;
    case UVIEW_MSG_TIMER:
    {
        // find timer
        uint32_t timer_id = uview_get_timer_id(msg);
        gui_timer_t *timer = gui_window_find_timer(window, timer_id);
        if (timer)
        {
            if (timer->callback)
            {
                bool timer_restart = timer->callback(spirit, timer_id, timer->calldata);
                if (timer_restart)
                {
                    gui_window_restart_timer(window, timer_id, -1);
                }
            }
            return 0;
        }
    }
    break;
    case UVIEW_MSG_MOVE:
    {
        gui_event_t event;
        event.type = GUI_WINMOVE_EVENT;
        event.winmove.x = uview_get_move_x(msg);
        event.winmove.y = uview_get_move_y(msg);
        if (gui_signal_emit_arg(spirit, "move_event", &event))
            return -1;
        return 0;
    }
    break;
    default:
        break;
    }
    return -1;
}

int gui_window_main(gui_spirit_t *spirit, uview_msg_t *msg)
{
    int x = uview_msg_get_mouse_x(msg) - spirit->x;
    int y = uview_msg_get_mouse_y(msg) - spirit->y;

    // mouse in navigant
    if (y < 0)
    {
        uview_set_mouse_status(spirit->view, UVIEW_MOUSE_NORMAL);
    }

    #ifdef DEBUG_GUI
    dprintf("[gui_window] main: spirit %x cur mouse pos %d %d\n",spirit,x,y);
    #endif
    
    switch (uview_msg_get_id(msg))
    {
    case UVIEW_MSG_MOUSE_MOTION:
        if(!gui_mouse_motion(spirit, x, y))
            return 0;
        break;
    case UVIEW_MSG_MOUSE_LBTN_DOWN:
        if (!gui_mouse_btn_down(spirit, UVIEW_BTN_LEFT, x, y))
            return 0;
        break;
    case UVIEW_MSG_MOUSE_MBTN_DOWN:
        if (!gui_mouse_btn_down(spirit, UVIEW_BTN_MIDDLE, x, y))
            return 0;
        break;
    case UVIEW_MSG_MOUSE_RBTN_DOWN:
        if (!gui_mouse_btn_down(spirit, UVIEW_BTN_RIGHT, x, y))
            return 0;
        break;
    case UVIEW_MSG_MOUSE_LBTN_UP:
        if (!gui_mouse_btn_up(spirit, UVIEW_BTN_LEFT, x, y))
            return 0;
        break;
    case UVIEW_MSG_MOUSE_MBTN_UP:
        if (!gui_mouse_btn_up(spirit, UVIEW_BTN_MIDDLE, x, y))
            return 0;
        break;
    case UVIEW_MSG_MOUSE_RBTN_UP:
        if (!gui_mouse_btn_up(spirit, UVIEW_BTN_RIGHT, x, y))
            return 0;
        break;
    case UVIEW_MSG_KEY_DOWN:
        if (!gui_keyboard_keydown(spirit, uview_msg_get_key_code(msg), uview_msg_get_key_modify(msg)))
            return 0;
        break;
    case UVIEW_MSG_KEY_UP:
        if (!gui_keyboard_keyup(spirit, uview_msg_get_key_code(msg), uview_msg_get_key_modify(msg)))
            return 0;
        break;
    default:
        break;
    }
    return -1; // need to be progressed
}

//used to top windows
int gui_window_draw_border(gui_window_t *window, int is_active, int win_back)
{
    if (!window)
        return -1;
    uview_color_t background, border, text;
    if (is_active) // active window
    {
        background = window->style->background_color_active;
        border = window->style->border_color_active;
        text = window->style->text_color_active;
        window->winflags |= GUI_WINDOW_ACTIVE;
    }
    else // inactive window
    {
        background = window->style->background_color_inactive;
        border = window->style->border_color_inactive;
        text = window->style->text_color_inactive;
        window->winflags &= ~GUI_WINDOW_ACTIVE;
    }

    gui_spirit_t *win_spirit = &window->window_spirit;
    gui_spirit_t *spirit = &window->spirit;
    assert(win_spirit->surface);
    assert(spirit);

    win_spirit->style.background_color = background;
    spirit->style.background_color = background;
    int border_thick = window->style->border_thick;

    if (win_back)
    {
        // redraw window background
        gui_surface_rectfill(win_spirit->surface, border_thick, border_thick, win_spirit->width - border_thick * 2, win_spirit->height - border_thick * 2, win_spirit->style.background_color);
    }

    // draw navigant
    gui_surface_rectfill(win_spirit->surface, border_thick, border_thick, win_spirit->width - border_thick * 2, window->style->navigation_height, background);

    // draw standard border
    gui_surface_rectfill(win_spirit->surface, 0, 0, win_spirit->width, border_thick, border);
    gui_surface_rectfill(win_spirit->surface, 0, 0, border_thick, win_spirit->height, border);
    gui_surface_rectfill(win_spirit->surface, 0, win_spirit->height - border_thick, win_spirit->width, border_thick, border);
    gui_surface_rectfill(win_spirit->surface, win_spirit->width - border_thick, 0, border_thick, win_spirit->height, border);

    // update navigation button,title,icon color
    gui_window_navigation_t *navigation = &window->navigation;
    if (navigation->title)
    {
        navigation->title->style.font_color = text;
    }

    gui_container_t *container = win_spirit->container;
    assert(container);
    gui_spirit_t *navigation_spirit;
    gui_button_t *btn;
    list_traversal_all_owner_to_next(navigation_spirit, &container->children_list, list)
    {
        if (navigation_spirit->type == GUI_SPIRIT_TYPE_BUTTON)
        {
            btn = GUI_BUTTON(navigation_spirit);

            btn->color_idle = background;
            if (!(btn->flags & GUI_BUTTON_COLOR_TOUCH_FIXED))
            {
                btn->color_touch = GUI_RGB_SUB(background, 0x40, 0x40, 0x40);
            }
            if (!(btn->flags & GUI_BUTTON_COLOR_CLICK_FIXED))
            {
                btn->color_click = GUI_RGB_SUB(background, 0x20, 0x20, 0x20);
            }
            navigation_spirit->style.background_color = btn->color_idle;

            // update color
            navigation_spirit->style.font_color = text;
        }
        else
        {
            if (navigation_spirit->type == GUI_SPIRIT_TYPE_LABEL)
            {
                navigation_spirit->style.background_color = background;
            }
        }
    }

    // flush border to screen
    uview_bitmap_t bmp;
    uview_bitmap_init(&bmp, win_spirit->surface->w, win_spirit->surface->h, (uview_color_t *)win_spirit->surface->pixel);
    uview_bitblt_update(win_spirit->view, 0, 0,&bmp);

    gui_spirit_show_children(win_spirit);

    //flush context surface
    uview_bitmap_init(&bmp, spirit->surface->w, spirit->surface->h, (uview_color_t *)spirit->surface->pixel);
    uview_bitblt_update(spirit->view, spirit->x, spirit->y, &bmp);
    return 0;
}

//used to popup windows
int gui_window_draw_no_border(gui_window_t *window)
{
    if (!window)
        return -1;
    uview_color_t background = window->style->background_color_active;
    gui_spirit_t *spirit = &window->spirit;
    assert(spirit->surface);

    // draw context
    gui_surface_clear(spirit->surface);
    gui_surface_rectfill(spirit->surface, 0, 0, spirit->width, spirit->height, background);

    // flush screen
    uview_bitmap_t bmp;
    uview_bitmap_init(&bmp, spirit->surface->w, spirit->surface->h, (uview_color_t *)spirit->surface->pixel);
    uview_bitblt_update(spirit->view, 0, 0, &bmp);
    return 0;
}

static bool gui_window_close_button_event(gui_spirit_t *spirit, void *data)
{
    gui_window_t *window = (gui_window_t *)data;
    dprintf("[window] close btn down\n");
    gui_spirit_destroy(&window->spirit);
    return true;
}

static bool gui_window_minimum_button_event(gui_spirit_t *spirit, void *data)
{
    gui_window_t *window = (gui_window_t *)spirit;
    dprintf("[window] minimum btn down\n");
    gui_window_minimum(window);
    return true;
}

static bool gui_window_maximum_button_event(gui_spirit_t *spirit, void *data)
{
    gui_window_t *window = (gui_window_t *)data;
    dprintf("[window] maximum btn down");
    gui_window_maximum(window);
    return true;
}

static void draw_icon(gui_spirit_t *spirit, int *bits, uint32_t color)
{
    int i, j;
    for (j = 0; j < 14; j++)
    {
        for (i = 0; i < 14; i++)
        {
            if (bits[j] & (1 << i))
                    gui_surface_putpixel(spirit->surface,
                    spirit->width / 2 - 14 / 2 + 14 - 1 - i,
                    spirit->height / 2 - 14 / 2 + j,
                    color);    
        } 
    }
}

void gui_window_close_show(gui_spirit_t *spirit)
{
    draw_icon(spirit, ExitIcon, spirit->style.font_color);
}

void gui_window_minimum_show(gui_spirit_t *spirit)
{
    draw_icon(spirit, MinIcon, spirit->style.font_color);
}

void gui_window_maximum_show(gui_spirit_t *spirit)
{
    gui_window_t *win = (gui_window_t *)spirit->local;
    if (win->winflags & GUI_WINDOW_MAX)
    {
        draw_icon(spirit, NormalBackIcon, GUI_RGB(217, 217, 217));
        draw_icon(spirit, NormalIcon, spirit->style.font_color);
    }
    else
    {
        draw_icon(spirit, MaxBackIcon, GUI_RGB(217, 217, 217));
        draw_icon(spirit, MaxIcon, spirit->style.font_color);
    }
}

static int gui_window_create_navigation(gui_window_t *window)
{
    gui_window_navigation_t *navigation = &window->navigation;
    navigation->title = NULL;
    gui_spirit_t *window_spirit = &window->window_spirit;

    // create botton spirit
    gui_spirit_t *spirit_min = gui_button_create();
    assert(spirit_min);
    gui_spirit_t *spirit_max = gui_button_create();
    assert(spirit_max);
    gui_spirit_t *spirit_close = gui_button_create();
    assert(spirit_close);

    // modify width
    spirit_min->width = GUI_WINDOW_BTN_SIZE;
    spirit_max->width = GUI_WINDOW_BTN_SIZE;
    spirit_close->width = GUI_WINDOW_BTN_SIZE;

    navigation->maximum = spirit_max; //add maximum button point to navigation object 

    int x = window->style->border_thick;
    int y = window->style->border_thick + window->style->navigation_height / 2;
    gui_spirit_set_pos(spirit_close, x, y - spirit_close->height / 2);

    x += spirit_close->width+window->style->border_thick;
    gui_spirit_set_pos(spirit_min, x, y - spirit_min->height / 2);

    x += spirit_min->width+window->style->border_thick;
    gui_spirit_set_pos(spirit_max, x, y - spirit_max->height / 2);

    // close touch read color
    gui_button_t *close_btn = GUI_BUTTON(spirit_close);
    close_btn->color_touch = GUI_RGB(236, 17, 35);
    close_btn->color_click = GUI_RGB(136, 54, 104);
    close_btn->flags = GUI_BUTTON_COLOR_TOUCH_FIXED | GUI_BUTTON_COLOR_CLICK_FIXED;

    // draw bottom with callback
    spirit_close->show_bottom = gui_window_close_show;
    spirit_min->show_bottom = gui_window_minimum_show;
    spirit_max->show_bottom = gui_window_maximum_show;
    spirit_max->local = window;

    gui_container_add(GUI_CONTAINER(window_spirit), spirit_close);
    gui_container_add(GUI_CONTAINER(window_spirit), spirit_min);
    gui_container_add(GUI_CONTAINER(window_spirit), spirit_max);

    // set signal event
    gui_signal_connect(spirit_close, "button_press", GUI_CALLBACK(gui_window_close_button_event), NULL);
    gui_signal_connect(spirit_min, "button_press", GUI_CALLBACK(gui_window_minimum_button_event), NULL);
    gui_signal_connect(spirit_max, "button_press", GUI_CALLBACK(gui_window_maximum_button_event), NULL);

    return 0;
}

static int gui_window_navigation_button_width(gui_window_t *window)
{
    if (!window)
        return -1;
    gui_container_t *container = window->window_spirit.container;
    if (!container)
        return -1;
    gui_spirit_t *spirit = NULL;
    int width = 0;
    list_traversal_all_owner_to_next(spirit, &container->children_list, list)
    {
        if (spirit->type == GUI_SPIRIT_TYPE_BUTTON)
            width += spirit->width;
    }
    return width;
}

static inline int gui_window_destroy_navigation(gui_window_t *window)
{
    return gui_container_remove_destroy_all(window->window_spirit.container);
}

static gui_spirit_t *gui_window_create_toplevel(gui_window_t *window)
{
    int width = window->content_width;
    int height = window->content_height;

    // init
    int new_width = 0, new_height = 0;
    gui_window_calc_view_size(window, width, height, &new_height, &new_width);

    // window spirit
    gui_spirit_t *window_spirit = &window->window_spirit;
    gui_window_spirit_setup(window, window_spirit, 0, 0, new_width, new_height);

    // context spirit
    gui_spirit_t *spirit = &window->spirit;
    if(gui_window_spirit_setup(window, spirit, window->style->border_thick, window->style->border_thick + window->style->navigation_height, width, height)<0)
    {
        gui_window_spirit_setdown(window_spirit);
        return NULL;
    }
    
    // create navigation
    if (gui_window_create_navigation(window) < 0)
    {
        gui_window_spirit_setdown(window_spirit);
        gui_window_spirit_setdown(spirit);
        return NULL;
    }

    // alloc view from window
    if (gui_window_view_setup(window, 0, 0, new_width, new_height) < 0)
    {
        gui_window_destroy_navigation(window);
        gui_window_spirit_setdown(window_spirit);
        gui_window_spirit_setdown(spirit);
        return NULL;
    }

    gui_window_reset_mobile_area(window);

    // draw window to surface
    gui_surface_rectfill(spirit->surface, 0, 0, spirit->width, spirit->height, GUI_BLACK);

    gui_window_draw_border(window, 1, 1);

    #ifdef DEBUG_GUI
    dprintf("[gui_window] create toplevel window ok\n");
    #endif 
    return spirit;
}

static gui_spirit_t *gui_window_create_popup(gui_window_t *window)
{
    int width = window->content_width;
    int height = window->content_height;

    gui_spirit_t *spirit = &window->spirit;
    if (gui_window_spirit_setup(window, spirit, 0, 0, width, height) < 0)
    {
        return NULL;
    }

    if (gui_window_view_setup(window, 0, 0, width, height) < 0)
    {
        gui_window_view_setdown(window);
        return NULL;
    }

    gui_window_reset_mobile_area(window);

    //background rectfill 
    gui_surface_rectfill(spirit->surface, 0, 0, spirit->width, spirit->height, GUI_RED);

    gui_window_draw_no_border(window);

    #ifdef DEBUG_GUI
    dprintf("[gui_window] create popup window ok\n");
    #endif
    return spirit;
}

gui_spirit_t *gui_window_create(gui_window_type_t type)
{
    gui_window_t *window = malloc(sizeof(gui_window_t));
    if (!window)
        return -1;
    memset(window, 0, sizeof(gui_window_t));
    window->type = type;
    window->routine = NULL;
    window->paint_callback = NULL;
    window->style = &__gui_window_style_default;
    window->winflags = GUI_WINDOW_ENABLERESIZE;
    window->content_width = GUI_WINDOW_WIDTH_DEFAULT;
    window->content_height = GUI_WINDOW_HEIGHT_DEFAULT;
    window->extension = NULL;
    window->icon_msgid = -1;
    list_init(&window->timer_list_head);
    gui_surface_init(&window->mmap_surface, 0, 0, NULL);
    gui_rect_init(&window->invalid_rect, 0, 0, 0, 0);
    gui_rect_init(&window->backup_win_info, 0, 0, 0, 0);

    gui_spirit_t *spirit;
    if (type == GUI_WINDOW_TOPLEVEL)
    {
        spirit = gui_window_create_toplevel(window);
    }
    else
    {
        if (type == GUI_WINDOW_POPUP)
        {
            spirit = gui_window_create_popup(window);
        }
    }

    if (!spirit)
    {
        free(window);
        return NULL;
    }

    // create signal
    // list for window
    assert(!gui_signal_create(spirit, "delete_event"));
    assert(!gui_signal_create(spirit, "destroy_event"));
    assert(!gui_signal_create(spirit, "active"));
    assert(!gui_signal_create(spirit, "inactive"));
    assert(!gui_signal_create(spirit, "move_event"));
    assert(!gui_signal_create(spirit, "maximum_notify"));
    assert(!gui_signal_create(spirit, "minimum_notify"));
    assert(!gui_signal_create(spirit, "resize_event"));

    // mouse
    assert(!gui_signal_create(spirit, "button_press"));
    assert(!gui_signal_create(spirit, "button_release"));
    assert(!gui_signal_create(spirit, "motion_notify"));
    assert(!gui_signal_create(spirit, "button_scroll"));
    assert(!gui_signal_create(spirit, "enter_notify"));
    assert(!gui_signal_create(spirit, "leave"));

    // keyboard
    assert(!gui_signal_create(spirit, "key_press"));
    assert(!gui_signal_create(spirit, "key_release"));

    return spirit;
}

gui_spirit_t *gui_window_create_simple(char *title, int x, int y, int width, int height)
{
    gui_spirit_t *spirit = gui_window_create(GUI_WINDOW_TOPLEVEL);
    if (!spirit)
        return -1;
    assert(!gui_window_set_title(GUI_WINDOW(spirit), title));
    assert(!gui_window_set_default_size(GUI_WINDOW(spirit), width, height));
    assert(!gui_window_set_position_absolute(GUI_WINDOW(spirit), x, y));
    return spirit;
}

int gui_window_invalid_rect(gui_window_t *window, gui_rect_t *rect)
{
    if (!window)
        return -1;
    if (gui_rect_valid(&window->invalid_rect))
        gui_rect_merge(&window->invalid_rect, rect);
    else
        gui_rect_init(&window->invalid_rect, rect->x, rect->y, rect->w, rect->h);

    return 0;
}

int gui_window_invalid_window(gui_window_t *window)
{
    if (!window)
        return -1;
    gui_spirit_t *spirit = &window->spirit;
    gui_rect_init(&window->invalid_rect, 0, 0, spirit->width, spirit->height);
    return 0;
}

int gui_window_get_invalid(gui_window_t *window, gui_rect_t *rect)
{
    if (!window || !rect)
        return -1;
    if (gui_rect_valid(&window->invalid_rect))
    {
        *rect = window->invalid_rect;
        gui_rect_init(&window->invalid_rect, 0, 0, 0, 0);
        return 0;
    }
    return -1;
}

int gui_window_paint(gui_window_t *window)
{
    if (!window)
        return -1;
    if (gui_rect_valid(&window->invalid_rect))
    {
        int vid = -1;
        if (!uview_get_vid(window->spirit.view, &vid))
        {
            uview_msg_t msg;
            uview_msg_header(&msg, UVIEW_MSG_PAINT, vid);
            uview_send_msg(window->spirit.view, &msg);
        }
    }
    return 0;
}

int gui_window_show(gui_window_t *window)
{
    if (!window)
        return -1;
    uview_show(window->spirit.view);
    return 0;
}

int gui_window_set_icon(gui_window_t *window, const char *path, int type)
{
    if (!window)
        return -1;

    // connect ipc
    if (window->icon_msgid == -1)
    {
        window->icon_msgid = msgget(GUI_WINDOW_ICON_MSG_NAME, IPC_CREATE);
        if (window->icon_msgid < 0)
            return -1;
    }

    gui_view_t *view = gui_view_find(window->spirit.view);
    if (!view)
        return -1;
    // send path
    if (msgsend(window->icon_msgid, (void *)path, min(MAX_PATH_LEN, strlen(path)), 0))
        return -1;
    // send icon msg
    uview_msg_t msg;
    uview_msg_header(&msg, UVIEW_MSG_SETICON, view->real_view);
    uview_msg_data(&msg, 0, type, 0, 0);
    if (uview_send_msg(window->spirit.view, &msg) < 0)
        return -1;
    return 0;
}

int gui_window_spirit_setup(gui_window_t *window, gui_spirit_t *spirit, int x, int y, int width, int height)
{
    gui_spirit_init(spirit, x, y, width, height);
    gui_spirit_set_type(spirit, GUI_SPIRIT_TYPE_WINDOW);
    spirit->style.font_align = GUI_ALIGN_CENTER;
    spirit->style.background_color = window->style->background_color_inactive;
    spirit->style.cursor = UVIEW_MOUSE_NORMAL;
    gui_container_t *container = gui_container_create(spirit);
    if (!container)
    {
        dprintf("[gui_window] spirit container create err\n");
        return -1;
    }
    gui_spirit_set_container(spirit, container);

    // create surface
    gui_surface_t *surface = gui_surface_create(width, height);
    if (!surface)
    {
        dprintf("[gui_window] spirit create surface failed\n");
        gui_spirit_set_container(spirit, NULL);
        return -1;
    }
    gui_surface_clear(surface);
    gui_spirit_set_surface(spirit, surface);

    return 0;
}

int gui_window_spirit_setdown(gui_spirit_t *spirit)
{
    if (!spirit)
        return -1;
    gui_spirit_set_container(spirit, NULL);
    gui_spirit_cleanup(spirit);
    return 0;
}

int gui_window_view_setup(gui_window_t *window, int x, int y, int width, int height)
{
    // create view
    int view = uview_open(width, height, window->type == GUI_WINDOW_TOPLEVEL ? UVIEW_TYPE_WINDOWS : UVIEW_TYPE_FIXED);
    if (view < 0)
        return -1;
    uview_set_pos(view, x, y);
    
    gui_spirit_set_view(&window->spirit, view);
    if (window->type == GUI_WINDOW_TOPLEVEL)
    {
        gui_spirit_set_view(&window->window_spirit, view);
    }

    gui_view_t *pview = gui_view_create();
    assert(pview);
    pview->view = view;
    uview_get_vid(view, &pview->real_view);
    list_add_tail(&window->spirit.list, &pview->spirit_list_head);

    // add window spirit to same view
    if (window->type == GUI_WINDOW_TOPLEVEL)
    {
        list_add_tail(&window->window_spirit.list, &pview->spirit_list_head);
    }
    gui_view_add(pview);

    pview->spirit = &window->spirit;
    return 0;
}

int gui_window_view_setdown(gui_window_t *window)
{
    if (!window)
        return -1;
    gui_view_t *pview = gui_view_find(window->spirit.view);
    if (!pview)
        return -1;
    list_del(&window->spirit.list);
    if (window->type == GUI_WINDOW_TOPLEVEL)
    {
        list_del(&window->window_spirit.list);
    }
    pview->spirit = NULL;
    gui_view_remove(pview);
    #ifdef DEBUG_GUI
    dprintf("[window] hide window view %d\n",pview->view);
    #endif 
    uview_hide(pview->view);
    uview_close(pview->view);
    gui_view_destroy(pview);
    return 0;
}

int gui_window_paint_callback(gui_window_t *window, gui_window_paint_callback_t callback)
{
    if (!window)
        return -1;
    window->paint_callback = callback;
    return 0;
}

int gui_window_destroy(gui_window_t *window)
{    
    dprintf("[window] destroy window object\n");

    if (!window)
        return -1;

    // destroy resource
    gui_window_destroy_all_timer(window);
    gui_window_munmap(window);

    if (gui_window_view_setdown(window) < 0)
        return -1;

    if (window->type == GUI_WINDOW_TOPLEVEL)
    {
        gui_window_destroy_navigation(window);
        gui_window_spirit_setdown(&window->window_spirit);
    }
    gui_window_spirit_setdown(&window->spirit);

    free(window);
    // check has window destroy
    __gui_has_window_close = 1;
    return 0;
}

int gui_window_set_title(gui_window_t *window, char *title)
{
    if (!window || !title)
        return -1;
    // unavalidable to set title
    if (window->type != GUI_WINDOW_TOPLEVEL)
        return -1;

    gui_window_navigation_t *navigation = &window->navigation;
    if (!navigation->title)
    {
        navigation->title = gui_label_create(title);
        if (!navigation->title)
            return -1;
        navigation->title->style.background_color = GUI_COLOR_NONE;
        navigation->title->style.font_align = GUI_ALIGN_LEFT;
                
        gui_label_set_text(navigation->title, title, strlen(title));

        // add to container
        gui_container_add(GUI_CONTAINER(&window->window_spirit), navigation->title);
    }
    else
    {
        // reset background color
        navigation->title->style.background_color = (window->winflags & GUI_WINDOW_ACTIVE) ? window->style->background_color_active : window->style->background_color_inactive;
        // rewrite text
        gui_label_set_text(navigation->title, title, strlen(title));
    }

    // redirect
    int x = window->style->border_thick * 2 + gui_window_navigation_button_width(window);
    int y = window->style->border_thick + window->style->navigation_height / 2 - navigation->title->height / 2;
    gui_spirit_set_pos(navigation->title, x, y);
    // show spirit
    gui_spirit_show(navigation->title);
    return;
}

int gui_window_set_resizeable(gui_window_t *window, bool resizeable)
{
    if (!window)
        return -1;

    gui_spirit_t *spirit = &window->spirit;
    if (resizeable)
    {
        window->winflags |= GUI_WINDOW_ENABLERESIZE;
        uview_set_resizeable(spirit->view);
    }
    else
    {
        window->winflags &= ~GUI_WINDOW_ENABLERESIZE;
        uview_set_unresizeable(spirit->view);
    }
    return 0;
}

int gui_window_set_monitor(gui_window_t *window, bool monitor)
{
    if (!window)
        return -1;
    gui_spirit_t *spirit = &window->spirit;
    return uview_set_monitor(spirit->view, monitor);
}

int gui_window_reset_mobile_area(gui_window_t *window)
{
    if (!window)
        return -1;
    int left, top, right, bottom;
    if (window->type == GUI_WINDOW_TOPLEVEL)
    {
        left = window->style->border_thick;
        left += gui_window_navigation_button_width(window);

        top = window->style->border_thick;
        right = window->window_spirit.width - window->style->border_thick;
        bottom = window->style->border_thick + window->style->navigation_height;
    }
    else
    {
        left = window->spirit.x;
        top = window->spirit.y;
        right = window->spirit.width;
        bottom = window->spirit.height;
    }

    uview_set_drag_region(window->spirit.view, left, top, right, bottom);
    return 0;
}

gui_surface_t *gui_window_get_surface(gui_window_t *window)
{
    if (!window)
        return NULL;
    return window->spirit.surface;
}

gui_surface_t *gui_window_get_mmap_surface(gui_window_t *window)
{
    if (!window)
        return -1;
    if (!window->mmap_surface.pixel || !window->mmap_surface.w || !window->mmap_surface.h)
    {
        printf("[gui_window] %s: get surface failed!\n", __func__);
        return NULL;
    }
    assert(window->mmap_surface.pixel);
    assert(window->mmap_surface.w);
    assert(window->mmap_surface.h);
    return &window->mmap_surface;
}

int gui_window_flip(gui_window_t *window)
{
    if (!window)
        return -1;
    gui_spirit_t *spirit = &window->spirit;
    uview_bitmap_t bmp;
    uview_bitmap_init(&bmp, spirit->surface->w, spirit->surface->h, spirit->surface->pixel);
    return uview_bitblt_update(spirit->view, spirit->x, spirit->y, &bmp);
}

int gui_window_update(gui_window_t *window, int x, int y, int w, int h)
{
    if (!window)
        return -1;
    gui_spirit_t *spirit = &window->spirit;

    if (x >= spirit->width)
        return 0;
    if (y >= spirit->height)
        return 0;
    if(w>spirit->width)
        w=spirit->width;
    if(h>spirit->height)
        h=spirit->height;

    int ex = x + w, ey = y + h;
    if (x < 0)
        x = 0;
    if (y < 0)
        y = 0;

    uview_bitmap_t bmp;
    uview_bitmap_init(&bmp, spirit->surface->w, spirit->surface->h, spirit->surface->pixel);
    return uview_bitblt_update_ex(spirit->view, spirit->x + x, spirit->y + y, &bmp, x, y, ex - x, ey - y);
}

int gui_window_refresh(gui_window_t *window, int x, int y, int w, int h)
{
    if (!window)
        return -1;

    gui_spirit_t *spirit = &window->spirit;
    if (x >= spirit->width)
        return 0;
    if (y >= spirit->height)
        return 0;
    int left = spirit->x + x;
    int top = spirit->y + y;
    int bottom = spirit->x + w;
    int right = spirit->y + h;
    return uview_update(spirit->view, left, top, right, bottom);
}

int gui_window_set_position(gui_window_t *window, gui_window_position_t pos)
{
    if (!window)
        return -1;
    int view = window->spirit.view;
    gui_spirit_t *spirit = &window->spirit;
    if (view < 0)
        return -1;
    switch (pos)
    {
    case GUI_WIN_POS_NONE:
    {
        int vx = 0, vy = 0;
        int x, y;
        uview_get_lastpos(view, &x, &y);
        uview_set_pos(view, vx, vy);
    }
    break;
    case GUI_WIN_POS_CENTER_ALWAYS:
        uview_set_unmoveable(view);
        break;
    case GUI_WIN_POS_CENTER:
    {
        int w = 0, h = 0;
        uview_get_screensize(view, &w, &h);
        uview_set_pos(view, w / 2 - spirit->width / 2, h / 2 - spirit->height / 2);
    }
    break;
    case GUI_WIN_POS_MOUSE:
    {
        int mx = 0, my = 0;
        uview_get_mousepos(view, &mx, &my);
        uview_set_pos(view, mx, my);
    }
    break;
    default:
        break;
    }
    return 0;
}

int gui_window_set_default_size(gui_window_t *window, int width, int height)
{
    if (!window)
        return -1;
    int vwidht, vheight;
    gui_window_calc_view_size(window, width, height, &vwidht, &vheight);
    uview_repair_size(&vwidht, &vheight);
    gui_window_calc_content_size(window, vwidht, vheight, &width, &height);

    // adjust spirit size
    if (gui_window_resize(window, width, height) < 0)
    {
        dprintf("gui windows resize return err\n");
        return -1;
    }
    if (gui_window_change_size(window, vwidht, vheight) < 0)
    {
        dprintf("gui window change size err\n");
        return -1;
    }
    return 0;
}

// set message routine
int gui_window_set_routine(gui_window_t *window, gui_window_routine_t routine)
{
    if (!window)
        return -1;
    window->routine = routine;
    return 0;
}

int gui_window_set_active(gui_window_t *window, bool is_active)
{
    if (!window)
        return -1;
    gui_window_draw_border(window, is_active, 0);
    return 0;
}

int gui_window_set_fixed(gui_window_t *window, bool fixed)
{
    if (!window)
        return -1;
    if (fixed)
    {
        // popup windosw could be set to fixed
        if (window->type == GUI_WINDOW_POPUP)
        {
            uview_set_type(window->spirit.view, UVIEW_TYPE_FIXED);
        }
        uview_set_unmoveable(window->spirit.view);
    }
    else
    {
        if (window->type == GUI_WINDOW_POPUP)
        {
            uview_set_type(window->spirit.view, UVIEW_TYPE_WINDOWS);
        }
        uview_set_moveable(window->spirit.view);
    }
    return 0;
}

int gui_window_set_size_minimum(gui_window_t *window, int width, int height)
{
    if (!window)
        return -1;
    int vwidth = 0, vheight = 0;
    gui_window_calc_view_size(window, width, height, &vheight, &vwidth);
    uview_set_size_min(window->spirit.view, width, height);
    return 0;
}

int gui_window_get_screen(gui_window_t *window, int width, int height)
{
    if (!window)
        return -1;
    return uview_get_screensize(window->spirit.view, width, height);
}

int gui_window_resize_to_screen(gui_window_t *window)
{
    if (!window)
        return -1;
    int width = 0;
    int height = 0;
    if (gui_window_get_screen(window, &width, &height) < 0)
    {
        printf("[gui_window] %s: get screen size failed!\n", __func__);
        return -1;
    }
    if (!width || !height)
    {
        printf("[gui_window] %s: get screen size error!\n", __func__);
        return -1;
    }
    return gui_window_set_default_size(window, width, height);
}

// load mouse cursor
int gui_window_load_mouse_cursor(gui_window_t *window, char *path)
{
    if (!window)
        return -1;
    if (window->spirit.view < 0)
        return -1;
    gui_mouse_load_cursors(window->spirit.view, path);
    return 0;
}

int gui_window_set_position_absolute(gui_window_t *window, int x, int y)
{
    if (!window)
        return -1;
    if (window->spirit.type == GUI_SPIRIT_TYPE_WINDOW)
    {
        uview_set_pos(window->spirit.view, x, y);
        return 0;
    }
    return -1;
}

int gui_window_get_position(gui_window_t *window, int *x, int *y)
{
    if (!window)
        return -1;
    if (window->spirit.view)
        return -1;
    return uview_get_pos(window->spirit.view, x, y);
}

int gui_window_enable_maximum(gui_window_t *window)
{
    if (!window)
        return -1;
    return gui_button_set_enable(GUI_BUTTON(window->navigation.maximum));
}

int gui_window_disable_maximum(gui_window_t *window)
{
    if (!window)
        return -1;
    return gui_button_set_disable(GUI_BUTTON(window->navigation.maximum));
}

int gui_window_set_maximum_rect(gui_window_t *window, gui_rect_t *rect)
{
    if (!window || !rect)
        return -1;
    if (window->spirit.view < 0)
        return -1;
    return uview_set_win_maxim_rect(window->spirit.view, rect->x, rect->y, rect->w, rect->h);
}

int gui_window_get_maximum_rect(gui_window_t *window, gui_rect_t *rect)
{
    if (!window || !rect)
        return -1;
    if (window->spirit.view < 0)
        return -1;
    return uview_get_win_maxim_rect(window->spirit.view, &rect->x, &rect->y, &rect->w, &rect->h);
}

int gui_window_maximum(gui_window_t *window)
{
    if (!window)
        return -1;

    gui_rect_t max_rect;
    // has been maximum window
    if (window->winflags & GUI_WINDOW_MAX)
        return -1;
    int wx = 0, wy = 0;
    gui_rect_init(&window->backup_win_info, wx, wy, window->window_spirit.width, window->window_spirit.height);

    if (gui_window_get_maximum_rect(window, &max_rect) < 0)
    {
        printf("[gui_window] get maximum rect failed!\n");
        return -1;
    }
    dprintf("maxrect %x %x %x %x\n",max_rect.x,max_rect.y,max_rect.w,max_rect.h);

    window->winflags |= GUI_WINDOW_MAX;
    // for MAXIMUM window,disable window resize
    if (window->winflags & GUI_WINDOW_ENABLERESIZE)
    {
        gui_window_set_resizeable(window, false);
        window->winflags |= GUI_WINDOW_DISABLERESIZE;
    }
    gui_window_calc_content_size(window, max_rect.w, max_rect.h, &max_rect.w, &max_rect.h);
    gui_window_resize(window, max_rect.w, max_rect.h);
    gui_window_change_size(window,max_rect.w,max_rect.h);
    gui_window_set_position_absolute(window, max_rect.x, max_rect.y);
    gui_signal_emit_by_name(&window->spirit, "maximum_notify");
    return 0;
}

int gui_window_maximum_selete(gui_window_t *window)
{
    if (!window)
        return -1;
    if (!window->winflags & GUI_WINDOW_MAX)
        return gui_window_maximum(window);
    else
        return gui_window_restore(window);
}

int gui_window_minimum(gui_window_t *window)
{
    if (!window)
        return -1;
    if (gui_spirit_hide(&window->spirit) < 0)
        return -1;
    gui_signal_emit_by_name(&window->spirit, "minimum_notify");
    return 0;
}

// restore window size
int gui_window_restore(gui_window_t *window)
{
    if (!window)
        return -1;
    gui_rect_t max_rect;
    if (!window->winflags & GUI_WINDOW_MAX)
    {
        // no maximum window status
        return -1;
    }
    max_rect = window->backup_win_info;
    window->winflags &= ~GUI_WINDOW_MAX;

    // clear disableresize flags
    if (window->winflags & GUI_WINDOW_DISABLERESIZE)
    {
        gui_window_set_resizeable(window, true);
        window->winflags &= ~GUI_WINDOW_DISABLERESIZE;
    }

    gui_window_calc_content_size(window, max_rect.w, max_rect.h, &max_rect.w, &max_rect.h);

    // reset size
    gui_window_resize(window, max_rect.w, max_rect.h);
    gui_window_set_position_absolute(window, max_rect.x, max_rect.y);
    gui_signal_emit_by_name(&window->spirit, "restore_notify");
    return 0;
}

uint32_t gui_window_add_timer(gui_window_t *window, uint32_t interval, gui_timer_callback_t func, void *data)
{
    if (!window)
        return 0;
    gui_timer_t *timer = gui_timer_create(interval, func, data);
    if (!timer)
        return 0;
    int timer_id = uview_add_timer(window->spirit.view, interval);
    if (timer_id < 0)
    {
        gui_timer_destroy(timer);
        return 0;
    }
    timer->timer_id = timer_id;
    list_add_tail(&timer->list, &window->timer_list_head);
    return timer->timer_id;
}

int gui_window_remove_timer(gui_window_t *window, uint32_t timer_id)
{
    if (!window)
        return -1;
    gui_timer_t *timer = gui_window_find_timer(window, timer_id);
    if (!timer)
        return -1;
    list_del_init(&timer->list);
    uview_del_timer(window->spirit.view, timer->timer_id);
    gui_timer_destroy(timer);
    return 0;
}

int gui_window_restart_timer(gui_window_t *window, uint32_t timer_id, int interval)
{
    if (!window)
        return -1;
    gui_timer_t *timer = gui_window_find_timer(window, timer_id);
    if (!timer)
        return -1;
    if (interval <= 0)
        interval = timer->interval;
    return uview_restart_timer(window->spirit.view, timer_id, interval);
}

static int gui_window_destroy_all_timer(gui_window_t *window)
{
    if (!window)
        return -1;
    gui_timer_t *timer, *timer_next;
    list_traversal_all_owner_to_next_safe(timer, timer_next, &window->timer_list_head, list)
    {
        list_del_init(&timer->list);
        uview_del_timer(window->spirit.view, timer->timer_id);
        gui_timer_destroy(timer);
    }

    return 0;
}

static gui_timer_t *gui_window_find_timer(gui_window_t *window, uint32_t timer_id)
{
    if (!window)
        return NULL;
    gui_timer_t *timer;
    list_traversal_all_owner_to_next(timer, &window->timer_list_head, list)
    {
        if (timer->timer_id == timer_id)
            return timer;
    }
    return NULL;
}

int gui_window_mmap(gui_window_t *window)
{
    if (!window)
        return -1;
    gui_spirit_t *spirit;
    if (window->type == GUI_WINDOW_TOPLEVEL)
    {
        spirit = &window->window_spirit;
    }
    else
    {
        spirit = &window->spirit;
    }
    size_t view_size = spirit->width * spirit->height * sizeof(gui_color_t);
    void *addr = cmmap(spirit->view, view_size, 0);
    if (addr == (void *)-1)
        return -1;
    gui_surface_init(&window->mmap_surface, spirit->width, spirit->height, addr);
    return 0;
}

int gui_window_munmap(gui_window_t *window)
{
    if (!window)
        return -1;

    if (!window->mmap_surface.pixel)
        return -1;
    if (!cmunmap(window->mmap_surface.pixel, window->mmap_surface.w * window->mmap_surface.h * sizeof(gui_color_t)))
        gui_surface_init(&window->mmap_surface, 0, 0, NULL);
    return 0;
}

int gui_window_mmap_resize(gui_window_t *window, int width, int height)
{
    if (!window)
        return -1;
    if (!window->mmap_surface.pixel) // no map
        return 0;

    size_t vsize = width * height * sizeof(gui_color_t);
    void *addr = cmmap(window->spirit.view, vsize, 0);
    if (addr == (void *)-1)
        return -1;
    if (gui_window_munmap(window) < 0)
    {
        cmunmap(addr, vsize);
        return -1;
    }
    gui_surface_init(&window->mmap_surface, width, height, addr);
    return 0;
}
